package com.weixin.project.Service;

import com.weixin.project.Common.Enum.UserType;
import com.weixin.project.Common.Exception.CurrentUserNullException;
import com.weixin.project.Dao.UserDao;
import com.weixin.project.Entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class UserService {
    @Autowired
    UserDao userDao;
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    FavoriteService favoriteService;
    @Autowired
    PracticeService practiceService;

    private static final String USER = "user";
    //string，值为用户id，expire为三小时
    //TODO:还是改成hash存吧
    private static final String USER_ID = "user_id";
    //TODO:是否需要清理有待商榷
    //zset，key为登录用户的id，value为时间
    private static final String USER_LOGIN_ZSET = "user_login";
    //set，值为用户id，只用来保存用户今天是否登录过，作用只是用来维护用户的登录天数
    private static final String USER_LOGIN_TODAY_SET = "user_login_today";
    @Cacheable(value = USER, key = "#id", unless = "#result == null")
    public User findUser(Integer id) {
        Optional<User> userOptional = userDao.findById(id);
        return userOptional.orElse(null);
    }

    public User findUserByOpenId(String openId) {
        return userDao.getUserByOpenId(openId);
    }

    @CachePut(value = USER, key = "#result.id", unless = "#result == null ")
    public User storeUser(User user) {
        if (redisTemplate.hasKey(USER + "::" + user.getId()))
            redisTemplate.delete(USER + "::" + user.getId());
        return userDao.save(user);
    }

    /**
     * 缓存userId - id，每次登录时调用一次
     * @param userId 用户id
     * @param id 用户数据库自增id
     */
    private void cacheUserId(String userId, Integer id){
        String key = USER_ID + "::"+userId;
        if (!redisTemplate.hasKey(key)) {
            redisTemplate.boundValueOps(key).set(id, 180, TimeUnit.MINUTES);
        }
        redisTemplate.expire(key, 180, TimeUnit.MINUTES);
        String zsetKey = USER_LOGIN_ZSET;
        key = "key::" + id;
        redisTemplate.boundZSetOps(zsetKey).add(key, new Date().getTime());
    }

    /**
     * 获取当前登录的用户
     * @param userId userId
     * @return 当前登录的用户
     */
    public User findCurrentUser(String userId) {
        String key = USER_ID + "::" + userId;
        if (!redisTemplate.hasKey(key)) {
            return null;
        }
        Integer id = Integer.parseInt(redisTemplate.boundValueOps(key).get().toString());
        //每次调用findCurrentUser时，说明用户处于登录状态，应该延长用户信息在缓存中的生命
        redisTemplate.expire(key, 180, TimeUnit.MINUTES);
        String zsetKey = USER_LOGIN_ZSET;
        key = "key::" + id;
        redisTemplate.boundZSetOps(zsetKey).add(key, new Date().getTime());
        return findUser(id);
    }

    public User findCurrentUser(HttpServletRequest request){
        if (request.getAttribute("current_user") == null){
            throw new CurrentUserNullException();
        }
        User user;
        try{
            user = ((User) request.getAttribute("current_user"));
        }catch (ClassCastException e){
            throw new CurrentUserNullException();
        }
        return user;
    }

    public void loadUserData2Redis(Integer userId) {
        favoriteService.loadUserFavorite2Redis(userId);
        practiceService.loadUserPractices2Redis(userId);
    }

//    public Set<User> findAllOnlineUsers(){
//        String zsetKey = USER_LOGIN_ZSET;
//        Date d = new Date();
//        Set<String> set = redisTemplate.boundZSetOps(zsetKey).rangeByScore(d.getTime() - 1000 * 3600 * 3, d.getTime());
//        Set<User> users = set.stream().map(x -> findUser(Integer.parseInt(x.split("::")[1]))).collect(Collectors.toSet());
//        return users;
//    }

    public User visitor(){
        return userDao.getUsersByType(UserType.VISITOR);
    }

    public void login(User user){
        cacheUserId(user.getUserId(), user.getId());
        incLoginDaysOfUser(user.getId());
        loadUserData2Redis(user.getId());
    }

    public void incLoginDaysOfUser(Integer userId){
        String setKey = USER_LOGIN_TODAY_SET;
        //如果有则说明已经登录过了
        if (redisTemplate.boundSetOps(USER_LOGIN_TODAY_SET).isMember(userId)){
            return;
        }
        else {
            User user = findUser(userId);
            user.setLogin(user.getLogin() + 1);
            storeUser(user);
            redisTemplate.boundSetOps(USER_LOGIN_TODAY_SET).add(userId);
        }
    }

    public void clearTodayLoginSetInRedis(){
        redisTemplate.delete(USER_LOGIN_TODAY_SET);
    }

}

